# -*- encoding:utf8 -*-
from timeproc import get_shoulde_checktimes
from attendance.models import AttendRecord,LeaveModel,AvaiLeave
from attendance.convert import t2i,date2str,str2date,i2t
from workday import is_workday
import re
from attendance.tool_func import get_or_new,get_or_none

def record(staff, date, timestr=None, noonrest="12:30-13:30"):
    """根据【1】考勤基础数据，【2】请假数据，
       计算当天考勤情况。
    Args: 参数主要来自于打卡机的原始记录
    staff: StaffInfo对象,
    date: str, 2015-12-31
    timestr:打卡记录
    noonrest:中午休息
    
    Return:
    所有计算数据存放在AttendRecord数据表
    """
    if timestr is None:
        record=get_or_none(AttendRecord,staff=staff,date=date2str(date) )
        if record:
            timestr=record.timestr
        else:
            return False
    #lastday_overtime = get_lastday_overtime(date, staff)
    workstart, workleave = parse_timestr(timestr)
    first_check,last_check=workstart,workleave
    
    # 考虑misscheck，防止产生absent的情况
    workstart, workleave=_take_leave(staff, date,workstart,workleave)
      
    workday, specialshift = is_workday(date)
    if specialshift:
        workshift=specialshift
    else:
        workshift=staff.workshift
    if workday:
        mt = re.match(r"(.*)-(.*)", workshift)
        if mt:
            dc=_workday_workshift_staff(staff,date,workstart,workleave,workshift,noonrest)
        else:  # workshif == flexable的人
            dc=_workday_flexiable(workstart,workleave)
    else:
        dc=_not_workday(workstart,workleave)
        _update_swap_off(dc['overtime'], date, staff)
        
    # --------- 清除新员工的特殊情况--------------------
    if date > str2date(staff.on_board_date):
        _save_all_result(staff, date, workstart,workleave,first_check,last_check,timestr,dc)
    elif date == str2date(staff.on_board_date):
        _save_org_timestr(staff, date,first_check,last_check,timestr)

    else:
        # 新员工入职前的考勤，无效，如果数据库中有，就删除他
        record_row = get_or_none(AttendRecord, staff=staff, date=date)
        if record_row:
            record_row.delete()

def parse_timestr(timestr):
    """
    timestr: str类型，"8:20 12:45 13:20 17:50"
    
    返回： t2i(8:20),t2i(17:50)
    """
    if not timestr:
        return 0, 0
    else:
        ls = [t2i(t) for t in timestr.split()]
        start, end = min(ls), max(ls)
        return start, end

def _take_leave(staff,date,workstart,workleave):
    """根据漏打卡，外出办公，调整员工实际工作时间"""
    misstimes=[]
    inwork_type=['miss_check','out_of']
    for miss in LeaveModel.objects.filter(staff=staff,start_time__startswith=date,category__in=inwork_type):
        if miss.submited_leave.status=='approved':
            miss_start_time=t2i(miss.start_time[-5:])
            miss_end_time=t2i(miss.end_time[-5:])
            misstimes.append(miss_start_time)
            misstimes.append(miss_end_time)
    if misstimes:
        workstart=min(misstimes)
        workleave=max(misstimes)
    return workstart,workleave

def _update_swap_off(overtime,date_,staff):
    datestr=date2str(date_)
    if overtime:
        swap = get_or_new(AvaiLeave, staff=staff, category='swap_off', start_date=datestr)
        swap.span = i2t(overtime)
        swap.save()    

def _workday_workshift_staff(staff,date,workstart,workleave,workshift,noonrest='12:30-13:30'):
    sud_start, sud_leave = get_shoulde_checktimes(staff, date,workshift,noonrest)
    # -------------开始计算考勤项-----------------------------
    absent = get_absent(workstart, workleave, sud_start,sud_leave)
    if absent != 0:  # 只要有旷工，就不去计算他的迟到，早退
        late = 0
        early_leave = 0
    else:
        late = get_late_time(sud_start, workstart)
        early_leave = get_early_leave(sud_leave, workleave)
    late_person = get_late_person(late)
    late_team = get_late_team(late)
    late_level = get_sub_sequence(late)
    workspan = get_workspan(workstart, workleave)
    overtime = get_overtime(workshift, workstart, workleave)
    return {'late':late,
            'late_level':late_level,
            'absent':absent,
            'late_person':late_person,
            'late_team':late_team,
            'early_leave':early_leave,
            'workspan':workspan,
            'overtime':overtime,
            
    }

def _workday_flexiable(workstart,workleave):
    return {'late':0,
            'late_person':0,
            'late_team':0,
            'late_level':'',
            'early_leave':0,
            'workspan':workleave-workstart,
            'overtime':0,
            'absent':0
    }

def _not_workday(workstart,workleave):
    return {'late':0,
            'late_person':0,
            'late_team':0,
            'late_level':'',
            'early_leave':0,
            'workspan':workleave-workstart,
            'overtime':workleave-workstart,
            'absent':0
    }

def _save_all_result(staff,date,workstart,workleave,firstcheck,lastcheck,timestr,kw):
    record_row = get_or_new(AttendRecord, staff=staff, date=date)
    record_row.enter_time = i2t(firstcheck)
    record_row.leave_time = i2t(lastcheck)
    record_row.workspan = i2t(kw.get('workspan'))
    record_row.late_level = kw.get('late_level')
    record_row.late_person = i2t(kw.get( 'late_person') )
    record_row.late_team = i2t(kw.get( 'late_team') )
    record_row.overtime = i2t(kw.get('overtime') )
    record_row.absent = i2t( kw.get('absent') )
    record_row.early_leave = i2t(kw.get('early_leave'))
    record_row.timestr = timestr
    record_row.save()   


def _save_org_timestr(staff, date,firstcheck,lastcheck,timestr):
    record_row = get_or_new(AttendRecord, staff=staff, date=date)
    record_row.enter_time = i2t(firstcheck)
    record_row.leave_time = i2t(lastcheck)
    record_row.timestr = timestr
    record_row.save()

def get_late_time(sud_start, workstart):
    """
    Args:
    @sud_start，int类型，should start,某员工合乎规定的上班时间
    @workstart,int类型，某员工当天第一次打卡的时间

    Return:
    @late,int类型,员工当天的迟到时间
    """
    if sud_start == 0:
        return 0
    elif workstart == 0:
        return 0  # 没打卡记录，算旷工一天，旷工后，不计算其迟到了
    else:
        return max(0, workstart - sud_start)


def get_late_team(late):
    """计算迟到引起的团队迟到时间

    Args:
    @late:int类型，迟到时间

    Return:
    @late_team:int类型，迟到引起的团队迟到时间

    计算规则：
    1. 迟到时间 <= 15分钟，不计算团队迟到时间
    2. 迟到时间 > 15分钟的部分，全部计入团队迟到时间
    """
    if 0 <= late <= 15:
        return 0
    else:
        return late - 15


def get_late_person(late):
    """计算迟到引起的个人迟到时间

    Args:
    @late:int类型，迟到的分钟数

    Return:
    @late_person:int类型，按照公司规定，惩罚后的个人迟到分钟数

    计算规则：
    1. 迟到<=15分钟，不计算个人迟到时间
    2. 15分钟< 迟到<=1个小时，扣除最初的15分钟，后面的时间全部累积到个人迟到时间
    3. 1个小时<迟到<=2个小时，扣除最初的15分钟，后面的时间*双倍*累积到个人迟到时间
    4. 迟到>2个小时，扣除最初的15分钟，后面的时间*三倍*累积到个人迟到时间
    """
    if 0 <= late <= 15:
        return 0
    elif 15 < late <= 60:
        return late - 15
    elif 60 < late <= 120:
        return (late - 15) * 2
    elif 120 < late:
        return (late - 15) * 3
    else:
        raise ValueError('late range error')


def get_sub_sequence(late):
    """计算迟到等级
    Args:
    @late:int类型，迟到的分钟数

    Return:
    @late_level，字符串，表示迟到等级，有：空,late1,late2,late3,late4

    计算规则:
    late1:迟到15分钟以内
    late2:迟到16分钟-60分钟
    late3:迟到61分钟-90分钟
    late4:迟到91分钟以上
    """
    if 0 < late <= 15:
        return 'late1'
    elif 15 < late <= 60:
        return 'late2'
    elif 60 < late <= 120:
        return 'late3'
    elif 120 < late:
        return 'late4'
    else:
        return ''


def get_overtime(workshift, workstart, workleave):
    """计算工作日加班时长

    Args:
    @workshift:字符串，员工的作息类型，例如："Flexibale","8:30-17:30"
    @workstart:Int类型，员工的打卡时间。
    @workleave:Int类型，该员工有效的最后一次打卡时间

    Return:
    @overtime:int类型，加班时长。

    计算规则：
    工作日：
    1. 弹性工作员工，不计算加班
    2. 普通员工，从20：00后开始计算加班时长
    非工作日：
    以第一次打卡到最后一次打卡的时长，作为加班时间
    """
    if workshift.lower() != "flexible":
        return max(0, workleave - 20 * 60)  # 减去晚上8点
    else:
        return 0


def get_early_leave(sud_leave, workleave):
    """
    Args:
    @sud_leave:int类型，该员工合乎规定的下班时间
    @workleave:int类型,该员工有效的最后一次打卡时间

    Return:
    @early_leave:int类型，员工的早退时间

    计算规则：
    最后打卡时间 - 规定作息时间段，最后一段的结束时间
    """
    if sud_leave == 0:
        return 0
    elif workleave == 0:
        return 0  # 没有打卡记录的情况，算作旷工，不算早退
    else:
        return max(0, sud_leave - workleave)


def get_workspan(workstart, workleave, worktimes=''):
    """
    Args:
    @workstart,int类型，上班打卡时间
    @workleave,int类型，下班打卡时间
    @worktimes,字符串，规定的员工作息时间段，现在没用，考虑以后会精细计算员工工作时长，考虑从工作时长中，扣除中途请假
                等情况。所以，调用该函数时，最好传入 worktimes。

    计算规则：
    下班打卡时间 - 上班打卡时间 - 中午午休1个小时
    """
    return max(0, workleave - workstart - 60)  # 扣掉中午的一个小时


def get_absent(workstart, workleave, sud_start,sud_leave):
    """获取旷工小时数

    Args:
    @workstart:int类型，上班时间
    @workleave:int类型，下班时间

    Return:
    旷工小时数
    """
    if sud_start != sud_leave:
        if workstart == 0:  # 没有打卡记录
            span=sud_leave-sud_start
            if span>5*60:
                span-=60
            return span
        elif workstart == workleave:  # 只有一次打卡记录
            return min(4 * 60,sud_leave-sud_start)
        else:
            return 0
    else:
        return 0
